package hy.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import hy.beans.Product;
import hy.beans.Result;
import hy.dao.ProductDao;
import hy.service.ProductService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Service
@EnableAspectJAutoProxy(exposeProxy = true)//重要，把代理对象暴露出来
public class ProductServiceImpl implements ProductService {
    @Autowired
    private StringRedisTemplate tmp;
    @Autowired
    ProductDao dao;
    ScheduledExecutorService pool= Executors.newScheduledThreadPool(1);

    public List<Product> getAllProducts() {
        List<Product> list;
        //products是redis中一Map,其key为product_id值，value为product对象的json字符串
        if (tmp.hasKey("products")) {//从redis中加载数据
            list = new ArrayList<>();

            List<String> pro_str = (List<String>) (Object) tmp.opsForHash().values("products");
            for (String s : pro_str) {
                Product pr = JSON.parseObject(s, new TypeReference<Product>() {});
                list.add(pr);
            }
        } else {//从mysql中加载数据
            list = dao.selectAllProduct();
            for (Product p : list) {
                tmp.opsForHash().put("products", p.getId() + "", JSON.toJSONString(p));
            }
            tmp.expire("products",60, TimeUnit.SECONDS);
        }
        return list;
    }

    public Result addCart(String phone, String productId) {
        Result r = new Result();
        if (StringUtils.isEmpty(phone)) {
            return r;
        }
        if (tmp.opsForHash().size("cart_" + phone) > 0) {
            if (tmp.opsForHash().hasKey("cart_" + phone, productId)) {
                int number = Integer.parseInt((String) tmp.opsForHash().get("cart_" + phone, productId));
                tmp.opsForHash().put("cart_" + phone, productId, String.valueOf(number + 1));
            } else {
                tmp.opsForHash().put("cart_" + phone, productId, "1");
            }
        } else {
            tmp.opsForHash().put("cart_" + phone, productId, "1");
        }
        r.setSuccess(true);
        return r;
        //redis中hgetall cart_138
    }

  public String createOrder(String phone)throws Exception {
        if (StringUtils.isEmpty(phone)) {
            return "你没有登录";
        }
        Map<String, String> map_cart = (Map<String, String>) (Object) tmp.opsForHash().entries("cart_" + phone);
        System.out.println("购物车:" + map_cart);
        if(map_cart==null||map_cart.size()==0){
            return "购物车为空";
        }
        //修改库存前先删除缓存，重新读取，降低redis跟mysql不一致概率
        tmp.delete("products");

        //减库存
        //获取当前类的代理对象
        ProductService service = (ProductService) AopContext.currentProxy();
        String rs =service.transac(map_cart);

        //清空缓存
        /*1.删除redis中的products?
        在修改期间，其他线程可能又读取了数据，并放到缓存中，为了一致性，再次删除
        2.为什么要延迟删除？
        A.删除缓存，准备修改mysql
        B读数据，缓存没有，mysql中读
        A.修改完毕，再删除缓存
        B.把从mysql读到的错误数据写入缓存，从而导致A最后一次删除缓存的效果失效，mysql与缓存不一致
        3.为什么启用线程？提高性能
        */
        pool.schedule(
              ()->tmp.delete("products"),
                1,
                TimeUnit.SECONDS
        );
        tmp.delete("cart_" + phone);

        return rs;
    }

    //库存不足回滚(mysql中rollback)
    @Transactional(rollbackFor = Exception.class)
    public String transac(Map<String, String> map_cart)throws Exception {
        System.out.println("事务开启了");
        //根据购物车要购买的数量从mysql减库存
        Set<String>ids=map_cart.keySet();
        for (String id : ids) {
            Integer sal_num = Integer.valueOf(map_cart.get(id));
            //修改mysql库存
            if(dao.updateProductNumById(Integer.valueOf(id),sal_num)==0){
                throw new Exception("库存不足");
            }
        }
        return "success";
    }
}